﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Web.UI;
using System.Xml;
using System.Xml.Linq;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.WebControls;
using Microsoft.Web.CommandUI;
using QASPRibbon.Commands;
using QASPRibbon.Controls;
using QASPRibbon.Helpers;

namespace QASPRibbon
{
    /// <summary>
    /// Class for work with server ribbon.
    /// </summary>
    public class RibbonManager
    {
        /// <summary>
        /// Singleton instance of RibbonManager.
        /// </summary>
        public static RibbonManager Current
        {
            get { return Instance; }
        }

        /// <summary>
        /// Adds a QAT (Quick Access Toolbar) to the ribbon
        /// </summary>
        /// <param name="qat"></param>
        /// <param name="page"></param>
        public void AddQATToPage(RibbonQAT qat, Page page)
        {
            QATCollection.Add(qat);
            var ribbon = SPRibbon.GetCurrent(page);
            var qatLeft = ribbon.Controls.OfType<SPRibbonPeripheralContent>().FirstOrDefault(c => c.Location == "TabRowLeft");
            if (qatLeft == null) return;
            foreach (var button in qat.Controls.Where(x=>x.Location == RibbonQATLocation.Left).OrderBy(x=>x.Sequence))
            {
                qatLeft.Controls.Add(button);
            }
            var qatRight = ribbon.Controls.OfType<SPRibbonPeripheralContent>().FirstOrDefault(c => c.Location == "TabRowRight");
            if (qatRight == null) return;
            foreach (var button in qat.Controls.Where(x => x.Location == RibbonQATLocation.Right).OrderBy(x => x.Sequence))
            {
                qatRight.Controls.Add(button);
            }
        }

        /// <summary>
        /// Adds a Tab to the Ribbon.
        /// </summary>
        /// <param name="tab">Tab</param>
        /// <param name="page">Instance of a web page which contains SPRibbon control.</param>
        /// <param name="makeInitial">Make a tab initial (True) or not (False).</param>
        public void AddTabToPage(RibbonTab tab, Page page, bool makeInitial)
        {
            TabCollection.Add(tab);
            if (SPRibbon.GetCurrent(page) == null)
                throw new Exception("SPRibbon.GetCurrent returned null for the specified page!");
            var tabXml = tab.AsXElement().ToString();
            if (page.IsPostBack)
            {
                RaisePostBackEvent(page, new RibbonTabEventArgs(tab));
                tabXml = tab.AsXElement().ToString();
            }


            Debug.WriteLine("Ribbon Tab' Xml: \r\n" + tabXml);
            AddRibbonExtension(tabXml, page, "Ribbon.Tabs", makeInitial);
            AddGroupTemplatesRibbonExtensions(tab.Groups, page);

            var commands = new RibbonCommandRepository();
            commands.AddCommands(tab);
            List<string> arr;
            if (page.Items[CommandsArrayKey] == null)
            {
                page.Items[CommandsArrayKey] = new List<string>();
                arr = new List<string>();
            }
            else
            {
                arr = page.Items[CommandsArrayKey] as List<string>;
            }
            if (arr != null)
            {
                arr.Add(CommandsArrayKey + "-" + tab.Id);
                page.Items[CommandsArrayKey] = arr;
            }
            page.Items[CommandsArrayKey + "-" + tab.Id] = commands;

            page.PreRenderComplete -= PagePreRenderComplete;
            page.PreRenderComplete += PagePreRenderComplete;

            var rt = Type.GetType("Microsoft.SharePoint.WebControls.SPRibbon, Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c", false);
            Ribbon ribbon = page.Items[rt] as Ribbon;// SPRibbon.GetCurrent(page);
            
            ribbon.MakeTabAvailable("Ribbon." + tab.Id);
            if (makeInitial)
                ribbon.InitialTabId = "Ribbon." + tab.Id;
        }

        /// <summary>
        /// Adds a Tab to the Ribbon
        /// </summary>
        /// <param name="tab">Tab</param>
        /// <param name="page">Instance of a web page which contains SPRibbon control.</param>
        public void AddTabToPage(RibbonTab tab, Page page)
        {
            AddTabToPage(tab, page, false);
        }

        public void ReplaceElement(RibbonElement element, Page page, string parentId)
        {
            element.NameSpace = parentId;
            var elements = page.Items["Replacement"] as List<RibbonElement> ?? new List<RibbonElement>();
            elements.Add(element);
        }

        #region Internal

        internal void AddRibbonContextualTabToPage(RibbonContextualGroup definition, Page page)
        {
            page.PreRenderComplete -= PagePreRenderComplete;
            page.PreRenderComplete += PagePreRenderComplete;

            AddRibbonExtension(definition.AsXElement().ToString(), page, "Ribbon.ContextualTabs", false);
            AddGroupTemplatesRibbonExtensions(definition.Tabs.SelectMany(t => t.Groups), page);

            var commands = new RibbonCommandRepository();
            commands.AddCommands(definition);
            List<string> arr;
            if (page.Items[CommandsArrayKey] == null)
            {
                page.Items[CommandsArrayKey] = new List<string>();
                arr = new List<string>();
            }
            else
            {
                arr = page.Items[CommandsArrayKey] as List<string>;
            }
            if (arr != null)
            {
                arr.Add(CommandsArrayKey + "-" + definition.Id);
                page.Items[CommandsArrayKey] = arr;
            }
            page.Items[CommandsArrayKey + "-" + definition.Id] = commands;
        }

        #endregion

        #region Private

        const string CommandsArrayKey = "QASPRibbonCommands";

        private static readonly RibbonManager Instance = new RibbonManager();

        private RibbonManager() { }

        private static void RaisePostBackEvent(Page page, RibbonTabEventArgs e)
        {
            if (!page.IsPostBack) return;
            var eventTarget = page.Request.Params["__EVENTTARGET"];
            var eventArgument = page.Request.Params["__EVENTARGUMENT"];
            var control = e.Instance.AllControls.FirstOrDefault(c => c.FullId == eventTarget);
            if (control == null) return;
            if (control is RibbonComboBox)
            {
                var comboBox = ((RibbonComboBox)control);
                comboBox.DoOnAction(new RibbonComboBoxEventArgs(comboBox, eventArgument));
            }
            else if (control is RibbonColorPicker)
            {
                var colorPicker = ((RibbonColorPicker)control);
                colorPicker.DoOnAction(new RibbonColorPickerEventArgs(colorPicker, eventArgument));
            }
            else if (control is RibbonCheckBox)
            {
                var checkBox = ((RibbonCheckBox)control);
                var checkedFlag = eventArgument.Equals("true", StringComparison.InvariantCultureIgnoreCase);
                checkBox.Checked = checkedFlag;
                checkBox.DoOnAction(new RibbonCheckBoxEventArgs(checkBox, checkedFlag));
            }
            else if (control is RibbonButton)
            {
                var button = ((RibbonButton)control);
                button.DoOnAction(new RibbonButtonActionEventArgs(button));
            }
        }

        private static void PagePreRenderComplete(object sender, EventArgs e)
        {
            try
            {
                var page = sender as Page;
                var headers = page.Items[CommandsArrayKey] as List<string>;
                if (headers == null) return;
                foreach (var header in headers)
                {
                    var ns = header.Replace(CommandsArrayKey + "-", string.Empty);
                    var commands = page.Items[header] as RibbonCommandRepository;
                    if (commands.GetCommandsCount() > 0)
                        RegisterCommands(page, commands, ns);
                }
            }
            catch (Exception ex)
            {
                var diagSvc = SPDiagnosticsService.Local;
                diagSvc.WriteTrace(0, new SPDiagnosticsCategory("QASPRibbon", TraceSeverity.Monitorable, EventSeverity.Error),
                                        TraceSeverity.Monitorable,
                                        "Error occured: " + ex.Message + "\nStackTrace: " + ex.StackTrace);
            }
        }

        private static void AddGroupTemplatesRibbonExtensions(IEnumerable<RibbonGroup> groups, Page page)
        {
            Ribbon ribbon = SPRibbon.GetCurrent(page);
            var ribbonExtensions = new XmlDocument();

            foreach (var group in groups)
            {
                ribbonExtensions.LoadXml(group.TemplateXml);
                ribbon.RegisterDataExtension(ribbonExtensions.FirstChild, "Ribbon.Templates._children");
            }
        }

        private static void AddRibbonExtension(string xml, Page page, string parentId, bool makeInitial)
        {
            Ribbon ribbon = SPRibbon.GetCurrent(page);
            ribbon.CommandUIVisible = true;
            ribbon.Minimized = makeInitial ? false : ribbon.Minimized;
            var ribbonExtensions = new XmlDocument();
            ribbonExtensions.LoadXml(xml);
            ribbon.RegisterDataExtension(ribbonExtensions.FirstChild, parentId + "._children");
        }

        private static void RegisterCommands(Page page, RibbonCommandRepository commands, string ns)
        {
            page.ClientScript.RegisterClientScriptBlock(
                page.GetType(),
                "QASPRibbonCommands" + ns,
                ScriptHelper.GetCommandsScript(commands.GetCommands(), ns));

            page.ClientScript.RegisterClientScriptBlock(
                page.GetType(),
                "InitPageComponent" + ns,
                ScriptHelper.GetPageComponentScript(ns));

            page.ClientScript.RegisterClientScriptBlock(
                page.GetType(),
                "QASPRibbonAPI" + ns,
                ScriptHelper.GetTabClientAPI(commands, QATCollection, TabCollection, ns));

            commands.ClearCommands();
        }

        #endregion

        private static List<RibbonQAT> QATCollection = new List<RibbonQAT>();
        private static List<RibbonTab> TabCollection = new List<RibbonTab>();
    }
}
